﻿using System;
using System.Collections.ObjectModel;
using System.Linq;

namespace Notoric.ViewModel.Elements.CWNotation
{
    /// <summary>
    /// Adapts a <see cref="Model.Elements.CWNotation.MusicSectionInterior"/> instance to a Windows Presentation Foundation view.
    /// </summary>
    public class MusicSectionInterior : SectionInterior
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="MusicSectionInterior"/> instance.
        /// </summary>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="MusicSection"/>
        /// </para>
        /// <para>
        /// The element that directly owns this element.
        /// </para>
        /// </param>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="Model.Elements.CWNotation.MusicSectionInterior"/>
        /// </para>
        /// <para>
        /// The adapted model element instance.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="adaptedElement"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="NullReferenceException">
        /// <paramref name="owner"/> is <see langword="null"/>.
        /// The values of the <see cref="ViewModelElement.UndoStack"/> and <see cref="ViewModelElement.RedoStack"/> properties
        /// are passed directly to the base class (<see cref="ViewModelElement"/>) constructor. Thus, there is not
        /// an occasion to react with a more appropriate exception.
        /// </exception>
        public MusicSectionInterior(MusicSection owner, Model.Elements.CWNotation.MusicSectionInterior adaptedElement)
            : base(adaptedElement, owner, owner.UndoStack, owner.RedoStack)
        {
            systems = new ObservableCollection<System>();
            SymbolInserter = new CoreSymbolInserter(this);
            selectionCollection = new ObservableCollection<CoreSymbol>();
            selection = new ReadOnlyObservableCollection<CoreSymbol>(selectionCollection);

            foreach (Model.Elements.CWNotation.System system in adaptedElement.Systems)
                systems.Add(new System(this, system));

            adaptedElement.Systems.CollectionChanged += new EventHandler<Model.Collections.NotifyCollectionChangedEventArgs>(Systems_CollectionChanged);

            RegisterToOwnerPropertyChanged<MusicSection>();
        }

        #endregion

        #region ViewModelElement Members

        /// <inheritdoc/>
        public override string ElementNameKey
        {
            get { return elementNameKey; }
        }

        #endregion

        #region SectionInterior Members

        /// <inheritdoc/>
        protected override void ClearSelectionCore()
        {
            foreach (var symbol in selectionCollection)
                symbol.IsSelected = false;

            SelectedArc = null;
            SelectedProgressMark = null;
            selectionCollection.Clear();
        }

        #endregion

        #region Event Handlers

        /// <inheritdoc/>
        protected override void Owner_PropertyChanged(ViewModelElement owner, global::System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsSelected")
            {
                if (!((MusicSection)owner).IsSelected)
                    ClearSelection();
            }

            base.Owner_PropertyChanged(owner, e);
        }

        /// <summary>
        /// Occurs when the <see cref="Model.Elements.CWNotation.MusicSectionInterior.Systems"/> collection of the <see cref="ViewModelElement.AdaptedElement"/> has changed.
        /// </summary>
        /// <param name="sender">Object that has sent the event</param>
        /// <param name="e">Event data</param>
        private void Systems_CollectionChanged(object sender, Model.Collections.NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case Model.Collections.NotifyCollectionChangedAction.Add:
                {
                    int index = e.NewStartingIndex;

                    foreach (Model.Elements.CWNotation.System system in e.NewItems)
                    {
                        var vmSystem = new System(this, system);
                        systems.Insert(index++, vmSystem);
                    }
                    break;
                }
                case Model.Collections.NotifyCollectionChangedAction.Remove:
                    foreach (Model.Elements.CWNotation.System system in e.OldItems)
                        systems.Remove(systems.First(s => s.AdaptedElement == system));
                    break;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds a given symbol to the selection.
        /// </summary>
        /// <param name="symbol">
        /// <para>
        /// Type: <see cref="CoreSymbol"/>
        /// </para>
        /// <para>
        /// The added symbol.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="symbol"/> is <see langword="null"/>.
        /// </exception>
        public void AddToSelection(CoreSymbol symbol)
        {
            if (symbol == null)
                throw new ArgumentNullException("symbol");

            if (!symbol.IsSelected)
            {
                symbol.IsSelected = true;
                selectionCollection.Add(symbol);
            }

            if (!IsContentSelected)
                IsContentSelected = true;
        }
        
        /// <summary>
        /// Removes a given symbol from the selection.
        /// </summary>
        /// <param name="symbol">
        /// <para>
        /// Type: <see cref="CoreSymbol"/>
        /// </para>
        /// <para>
        /// The removed symbol.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="symbol"/> is <see langword="null"/>.
        /// </exception>
        public void RemoveFromSelection(CoreSymbol symbol)
        {
            if (symbol == null)
                throw new ArgumentNullException("symbol");

            selectionCollection.Remove(symbol);
            symbol.IsSelected = false;

            if (selection.Count == 0)
                IsContentSelected = false;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the currently selected arc.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Arc"/>
        /// </para>
        /// <para>
        /// The currently selected arc.
        /// </para>
        /// </value>
        public Arc SelectedArc
        {
            get { return selectedArc; }
            set
            {
                if (selectedArc != null)
                    selectedArc.IsSelected = false;

                selectedArc = value;

                if (selectedArc != null)
                    selectedArc.IsSelected = true;

                RaisePropertyChanged("SelectedArc");
            }
        }

        /// <summary>
        /// Gets or sets the selected dynamics progress mark.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="DynamicsProgressMark"/>
        /// </para>
        /// <para>
        /// The selected dynamics progress mark.
        /// </para>
        /// </value>
        public DynamicsProgressMark SelectedProgressMark
        {
            get { return selectedProgressMark; }
            set
            {
                if (selectedProgressMark != null)
                    selectedProgressMark.IsSelected = false;

                selectedProgressMark = value;

                if (selectedProgressMark != null)
                    selectedProgressMark.IsSelected = true;

                RaisePropertyChanged("SelectedProgressMark");
            }
        }

        /// <summary>
        /// Gets the collection of core symbols that are currently selected.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ReadOnlyObservableCollection{T}"/> of <see cref="CoreSymbol"/>
        /// </para>
        /// <para>
        /// The collection of core symbols that are currently selected.
        /// </para>
        /// </value>
        public ReadOnlyObservableCollection<CoreSymbol> Selection
        {
            get { return selection; }
        }

        /// <summary>
        /// Gets the symbol inserter.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="CoreSymbolInserter"/>
        /// </para>
        /// <para>
        /// Gets the symbol inserter for this music section.
        /// </para>
        /// </value>
        public CoreSymbolInserter SymbolInserter
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the collection of systems.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ObservableCollection{T}"/> of <see cref="System"/>
        /// </para>
        /// <para>
        /// The collection of systems.
        /// </para>
        /// </value>
        public ObservableCollection<System> Systems
        {
            get { return systems; }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds a user-friendly name of this element.
        /// </summary>
        private const string elementNameKey = "ViewModel.ElementNames.CWMusicSectionInterior";
        /// <summary>
        /// Holds the value of the <see cref="SelectedArc"/> property.
        /// </summary>
        private Arc selectedArc;
        /// <summary>
        /// Holds the value of the <see cref="SelectedProgressMark"/> property.
        /// </summary>
        private DynamicsProgressMark selectedProgressMark;
        /// <summary>
        /// Holds the current selection.
        /// </summary>
        private ReadOnlyObservableCollection<CoreSymbol> selection;
        /// <summary>
        /// Holds the current selection.
        /// </summary>
        private ObservableCollection<CoreSymbol> selectionCollection;
        /// <summary>
        /// Holds the value of the <see cref="Systems"/> property.
        /// </summary>
        private ObservableCollection<System> systems;

        #endregion
    }
}
